// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module aes256_gcm (
  input  logic clk,                 // system clock
  input  logic reset_n,             // system reset

  input  wire         avm_write,
  input  wire [31:0]  avm_writedata,
  input  wire [7:0]   avm_address,
  output wire         avm_waitrequest,
  input  wire         avm_read,
  output wire [31:0]  avm_readdata,
  output wire         avm_readdatavalid
);

logic [127:0] aes_data_in;
logic [127:0] aes_data_out, multH, ct_data_inv, ct_data_gcm;
logic [127:0] aes_auth_tag;
logic [127:0] aes_iv_in;
logic [255:0] aes_mask_seed;
logic [255:0] aes_key_in;
logic [127:0] aes_cipher_out;
logic [255:0] aes_working_key_debug;
logic [1:0]   cnt_aes_flush;
logic aes_busy, aes_busy_out, auth_tag_valid, data_valid, lastround, aes_flush, aes_flush_multH, aes_flush_done;
logic aes_start, aes_last, aes_op, load_expanded, aes_mask_seed_valid, aes_data_in_valid, aes_key_in_valid;
logic aes_iv_in_valid, aes_ctrl_reg_valid, auth_tag_gen, en_multH_gen, en_multH_gen_dly, en_multH_gen_posedge;

typedef enum logic [1:0] {
        MULTH_GEN_1, 
        AES_FLUSH,       
        WAIT
        } state_multH;
state_multH state_H;
logic multH_generated, multH_generated_latch;

 always @(posedge clk)
 begin
   en_multH_gen_dly <= en_multH_gen;
 end

 assign en_multH_gen_posedge = en_multH_gen & (!en_multH_gen_dly);

 always @(posedge clk or negedge reset_n)
 begin
   if (!reset_n)
   begin
     multH_generated_latch <= 1'b0;
   end
   else
   begin
     if (aes_flush_done == 1'b1)
     begin
       multH_generated_latch <= 1'b0;
     end
     else if (multH_generated == 1'b1)
     begin
       multH_generated_latch <= 1'b1;
     end
   end
 end
 //-----Flush the AES logic after generating multH value---------
 always @(posedge clk or negedge reset_n)
 begin
   if (!reset_n)
   begin
     state_H         <= MULTH_GEN_1;
     multH_generated <= 1'b0;
     aes_flush_multH <= 1'b0;
     cnt_aes_flush   <= 2'b00;
   end
   else
   begin
     case (state_H)
       MULTH_GEN_1 : begin
         if (lastround & en_multH_gen)
         begin
           state_H <= AES_FLUSH;
         end
         else
         begin
           state_H <= MULTH_GEN_1;
         end
         multH_generated <= 1'b0;
         aes_flush_multH <= 1'b0;
         cnt_aes_flush   <= 2'b00;
       end
       AES_FLUSH : begin
         cnt_aes_flush   <= cnt_aes_flush + 1;
         aes_flush_multH <= 1'b1;
         if (cnt_aes_flush == 2'b10) //there is no specific requirement to assert aes flush. The 3 clock value is random
         begin
           state_H <= WAIT;
           multH_generated <= 1'b1;
         end
         else
         begin
           state_H <= AES_FLUSH;
           multH_generated <= 1'b0;
         end
       end
       WAIT : begin
         cnt_aes_flush   <= 2'b00;
         aes_flush_multH <= 1'b0;
         if (en_multH_gen == 1'b0)
         begin
           state_H         <= MULTH_GEN_1;
           multH_generated <= 1'b0;
         end
         else
         begin
           state_H         <= WAIT;
           multH_generated <= 1'b1;
         end
       end
       default : begin
         state_H         <= MULTH_GEN_1;
         multH_generated <= 1'b0;
         cnt_aes_flush   <= 2'b00;
         aes_flush_multH <= 1'b0;
       end

     endcase
   end
 end
 //--------------------------------------------------------------

 aes256_gcm_csr inst_csr (
   .clk                  (clk),
   .reset_n              (reset_n),
                      
   .aes_data_in          (aes_data_in),
   .aes_data_out         (aes_data_out),
   .aes_auth_tag         (aes_auth_tag),
   .aes_iv_in            (aes_iv_in),
   .aes_mask_seed        (aes_mask_seed),
   .aes_key_in           (aes_key_in),
   .aes_busy             (aes_busy_out),
   .multH_generated      (multH_generated_latch),
   .auth_tag_valid       (auth_tag_valid),
   .lastround            (lastround), 
   .aes_flush_done       (aes_flush_done), 
                     
   .avm_write            (avm_write),
   .avm_writedata        (avm_writedata),
   .avm_address          (avm_address),
   .avm_waitrequest      (avm_waitrequest),
   .avm_read             (avm_read),
   .avm_readdata         (avm_readdata),
   .avm_readdatavalid    (avm_readdatavalid),
                       
   .aes_flush            (aes_flush),
   .aes_start            (aes_start),
   .aes_last             (aes_last),
   .aes_op               (aes_op),
   .aes_data_in_valid    (aes_data_in_valid),
   .aes_mask_seed_valid  (aes_mask_seed_valid),
   .aes_key_in_valid     (aes_key_in_valid),
   .aes_iv_in_valid      (aes_iv_in_valid),
   .aes_ctrl_reg_valid   (aes_ctrl_reg_valid)
  );

aes256_gcm_fsm inst_aes256_gcm_fsm (
   .clk                   (clk),
   .reset_n               (reset_n),
   .aes_busy              (aes_busy),
   .aes_flush             (aes_flush),
   .aes_start             (aes_start),
   .aes_last              (aes_last),
   .aes_data_in_valid     (aes_data_in_valid),
   .aes_mask_seed_valid   (aes_mask_seed_valid),
   .aes_key_in_valid      (aes_key_in_valid),
   .aes_iv_in_valid       (aes_iv_in_valid),
   .aes_ctrl_reg_valid    (aes_ctrl_reg_valid),
   .auth_tag_gen          (auth_tag_valid),
   .multH_generated       (multH_generated),

   .aes_busy_out          (aes_busy_out),
   .en_multH_gen          (en_multH_gen),
   .aes_flush_done        (aes_flush_done)
);

 logic aes_block_reset_n;
 assign aes_block_reset_n = ((!reset_n) | (aes_flush | aes_flush_multH)) ? 0 : 1;

 A_cse_ocs_dpa_aes10c_unit #(
   .OCS_AES_DPA_USE_CTECH(0),
   .AES_P(1'b0)
 ) inst_A_cse_ocs_dpa_aes10c_unit(
   .clk                   (clk                  ),
   .reset_n               (reset_n    ),
   .aes_key_in            (aes_key_in           ),
   .aes_iv_in             (aes_iv_in            ),
   .aes_data_in           ((en_multH_gen == 1'b1) ? 128'h0 : aes_data_in),
   .aes_mask_seed	  (aes_mask_seed        ),
   .aes_load_mask	  (aes_mask_seed_valid  ),
   .mask_logic_en	  (1'b1   	        ),
   .data_valid            (data_valid   ),
   .start                 (aes_data_in_valid || en_multH_gen_posedge),
   .aes_op                (1'b0                 ),
   .aes_load_iv           (aes_iv_in_valid      ),
   .aes_flush             (aes_flush | aes_flush_multH),
   .aes_internal_mode     (3'b010               ),
   .aes_ctr_m_bits        (2'b11                ),
   .DMA_Sync_Reset        (1'b0                 ),
   .i_key_size            (2'b10                ),
   .aes_busy              (aes_busy             ),
   .aes_data_out          (aes_data_out         ),
   .aes_cipher_out        (aes_cipher_out       ),
   .aes_working_key_debug (aes_working_key_debug),
   .lastround             (lastround            ),
   .load_expanded         (load_expanded        )
 );
 

 always @(posedge clk or negedge reset_n)
 begin
   if (reset_n == 1'b0)
   begin
     multH <= 'h0;
   end
   else
   begin
     if (lastround == 1'b1 && en_multH_gen == 1'b1)
     begin
       for (int i = 0, j = 127 ; i < 128; i++, j--) begin
         multH[j] <= aes_data_out[i];//Rev(H)
       end
     end
   end
 end

 assign ct_data_gcm = (aes_op == 1'b0) ? aes_data_out : aes_data_in;

 always @*
 begin
   if (!reset_n)
   begin
     ct_data_inv <= 'b0;
   end
   else
   begin
     if (lastround == 1'b1 || aes_data_in_valid == 1'b1)
     begin
       for (int i = 0, j = 127 ; i < 128; i++, j--) begin
         ct_data_inv[j] <= ct_data_gcm[i];//Rev(CT)
       end
     end
   end
 end

gcm inst_gcm(
   .clk           (clk),
   .reset_n       (reset_n),
   .aes_flush_done(aes_flush_done),
   .aes_op        (aes_op),
   .ct_valid      (lastround & (!en_multH_gen)), //The gcm block should not start while generating the multH so using !en_multH_gen.
   .ct_data       (ct_data_inv),
   .multH_valid   (lastround & en_multH_gen),
   .multH         (multH),
   .last_data     (aes_last),
   .auth_tag      (aes_auth_tag),
   .auth_tag_valid(auth_tag_valid)
);

endmodule
